-- ======================================================================
-- Hell Lua Bus
-- 
-- Copyright 2012-2013Hell-Prototypes
--
-- http://www.hellprototypes.com/
--
-- This is free software, licensed under the terms of the GNU General
-- Public License as published by the Free Software Foundation.
-- ======================================================================
hlb = require('libhlb')
printf = require "mod_print"
iHEX = require "mod_iHEX"

SIM_DEBUG = false

function device_open()
	local ret = hlb.usb_open(0x6666f0f3)
	if ret < 0 then
		print("Fail to open usb device: " .. ret)
		return false
	end
	
	return true
end

function device_close()
	local ret = hlb.usb_close()
	if ret < 0 then
		print("Fail to close usb device")
		return false
	end
	
	return true
end

function pause()
    io.write("\r\nAny key to continue ...")
    io.flush()
    io.read()
    print("")
end
-- -----------------------------------------------
function Read_dot_File(file_name)
	-- print("file_name: " .. file_name)
	local file = io.open(file_name, "r")

	local raw_data = {}
	local data = 0
	local line_num = 1

	for line in file:lines() do
		for i=1, line:len()/5 do
			 data = tonumber(line:sub(5 * i - 2 , 5 * i-1), 16)
			 if type(data) == "number" then
			 	raw_data[#raw_data + 1] = data
			 else
			 	print("Error to convert line: " .. line_num .. " of: " .. line:sub(5 * i - 2 , 5 * i-1) )
			 end
		end
		line_num = line_num + 1
	end

	file:close()

	return raw_data
end
-- -----------------------------------------------
function split_to_3byte(addr)
	local B2 = bit32.band(bit32.rshift(addr, 16), 0xFF)
	local B1 = bit32.band(bit32.rshift(addr, 8),  0xFF)
	local B0 = bit32.band(addr, 0xFF)
    
    return B2, B1, B0
end
-- -----------------------------------------------
function string_to_table(str)
    local tb = {}
    local index = 1
    for i=1, string.len(str) do
        tb [i] = string.byte(str, i)
    end
    return tb
end
-- -----------------------------------------------
function rom_write_cmd(rom_cmd)
    if SIM_DEBUG == true then
        for i=1, #rom_cmd do
            io.write(string.format("%02x ", rom_cmd[i]):upper())
        end
        print("\r\n----------------------")
        return true
    end
    if #rom_cmd > 64 then
        print("rom_write_cmd() fail: cmd len error" .. #rom_cmd)
        return false
    end
    local msg = {0x40, 0xb0, 0, 0, #rom_cmd, 500}
    local ret

    ret = hlb.usb_control_msg(rom_cmd, msg)
    if ret < 0 then
        print("\r\nrom_write_cmd() ret = " .. ret)
        return false
    end
    return true
end

function rom_read_feedback(read_len)
    if SIM_DEBUG == true then
        local ret = {}
        for i=1, read_len do
            ret[i] = i;
        end
        return ret
    end
    local msg = {0x40+0x80, 0xb1, 0, 0, read_len, 500}
    local ret
    local empty = {}

    ret = hlb.usb_control_msg(msg)
    if(type(ret) == "table") then
        return ret
    else
        print("\r\nrom_read_feedback() fail")
    end
    return empty
end
-- -----------------------------------------------
function rom_wait_busy()
    local cmd={0x05, 0xff}
    local st = {}
    while true do
        if rom_write_cmd(cmd) == false then
            return false
        end
        st = rom_read_feedback(2)
        if #st ~= 2 then
            return false
        end
        --print(string.format("status = %02x , %02x\r\n", st[1], st[2]):upper())
        if not bit32.btest(st[2] , 0x01) then
            return true
        end
    end
end
-- -----------------------------------------------
function rom_write_enable()
    local cmd={0x06}
    return rom_write_cmd(cmd)
end
-- -----------------------------------------------
function rom_32KB_block_erase(address)
    local cmd = {0x52, 0, 0, 0}
    cmd[2], cmd[3], cmd[4] = split_to_3byte(address)
    
    if rom_wait_busy() == false then
        return false
    end
    if rom_write_enable() == false then
        return false
    end
    return rom_write_cmd(cmd)
end
-- -----------------------------------------------
-- program 60B(Max) to address
function rom_page_program_cmd(address, cmd_data)
    local index = 5
    local cmd = {0x02, 0, 0, 0}
    cmd[2], cmd[3], cmd[4] = split_to_3byte(address)
    for i=1, #cmd_data do
        cmd[index] = cmd_data[i]
        index = index + 1
    end
    if rom_wait_busy() == false then
        return false
    end
    if rom_write_enable() == false then
        return false
    end
    return rom_write_cmd(cmd)
end
-- -----------------------------------------------
function rom_page_program(start_addr, page_data)
    local index = 1
    local address = start_addr
    local cmd_data = {}
    
    if #page_data > 256 then
        return false
    end
    while index <= #page_data do
        for i=1, 60 do
            cmd_data[i] = page_data[index]
            index = index + 1
            if index > #page_data then
                break
            end
        end

        if rom_page_program_cmd(address, cmd_data) == false then
            return false
        end
        address = address + #cmd_data
        cmd_data = {}
    end
    return true
end
-- -----------------------------------------------
function rom_program(start_addr, pgm_data)
    local index = 1
    local address = start_addr
    local page_data = {}
    local percent, last_percent

    print("ROM Programing ... len: " .. #pgm_data)
    while index <= #pgm_data do
        for i=1, 256 do
            page_data[i] = pgm_data[index]
            index = index + 1
            if index > #pgm_data then
                break
            end
        end
        --io.write(".")
        percent = math.floor((100*index)/#pgm_data)
		if percent ~= last_percent then
			last_percent = percent
			io.write(string.format("%%%02d ", percent))
		end

        if rom_page_program(address, page_data) == false then
            print("rom_page_program() FAIL， address = " ..address.. "\r\n")
            return false
        end
        address = address + #page_data
        page_data = {}
    end
    print("\r\n")
    return true
end
-- -----------------------------------------------
-- read 60B(Max) from address
function rom_read_cmd(address, len)
    local index = 5
    local cmd = {0x03, 0, 0, 0}
    local ret = {}
    cmd[2], cmd[3], cmd[4] = split_to_3byte(address)
    for i=1, len do
        cmd[index] = 0x00
        index = index + 1
    end
    if rom_wait_busy() == false then
        return ret
    end

    if rom_write_cmd(cmd) == false then
        return ret
    end

    local fb = rom_read_feedback(len + 4)
    if #fb == 0 then
        return ret
    end

    for i=1,len do
        ret[i] = fb[i+4]
    end
    return ret
end
-- -----------------------------------------------
function rom_read_data(start_addr, len)
    local index = 1
    local read_back = {}
    local read_len
    local addr = start_addr
    local buffer = {}
    local percent, last_percent

    print("ROM Reading ...")
    while(index <= len) do
        if len - index >= 60 then
            read_len = 60
        else
            read_len = len - index + 1
        end
        read_back = rom_read_cmd(addr, read_len)

        addr = addr + read_len
        if #read_back ~= read_len then
            read_back = {}
            return read_back
        end
        for i=1, #read_back do
            buffer[index] = read_back[i]
            index = index + 1
        end
        --io.write(".")
        percent = math.floor((1000*index)/len)
		if percent ~= last_percent then
			last_percent = percent
			io.write(string.format("%%%0.1f ", percent/10))
		end
    end

    print("\r\n")
    return buffer
end
-- -----------------------------------------------
-- -----------------------------------------------
if SIM_DEBUG == true then
    --rom_page_program_cmd
    --rom_64KB_block_erase(0x112233)
    --[[
    local pgm_data = {}
    for i=1, 200 do
        pgm_data[i] = bit32.band(i, 0xFF)
    end
    rom_page_program(0, pgm_data)
    --]]
    --local ret = rom_read_data(0, 121)

    return
end
ret = device_open()
if ret == false then
    print("device_open fail")
    return
end

--download font dot matrix
if false then
    -- download dox 8x16
    dot_8x16 = Read_dot_File("ascii_8x16_dot.txt")
    --printf.table(dot_8x16, true)
    
    start_addr = 0xf8000
    rom_32KB_block_erase(start_addr)
    rom_program(start_addr, dot_8x16)
    ret = rom_read_data(start_addr, 1600)

    printf.table(ret, true)
end

--Download font
if false then
    local file = io.open("hwatch_font.bin", "rb")
    local font = file:read("*all")
    file:close()
    local l_dot = {}
    for i=1, font:len() do
        l_dot[i] = font:byte(i)
    end
    local start_addr = (1024-256)*1024
    for i=0, 7 do--256K
        rom_32KB_block_erase(start_addr + i*32*1024)
    end
    rom_program(start_addr, l_dot)

    ret = rom_read_data(start_addr, 128)
    printf.table(ret, true)
    ret = rom_read_data(start_addr + font:len() - 128, 128)
    printf.table(ret, true)
end

--download app name for test
if false then
    app_names = {
    "APP1\nhell1", "APP2\nhell2", "app3__0123456789\nhell3", "app4\nhell4",
    "app5\nhell5", "app6\nhell6", "app7\nhell7", "app8",
    "app9", "app_10\nhell10", "app_11\nhell11", "app_12\nhell12",
    "app_13\nhell a", "app_14\nhell b", "app_15\nhell c", "app_16\nhell dddd",
    }
    block_32KB_len = 32*1024
    start_addr = block_32KB_len - 64
    for key,value in pairs(app_names) do
        print(value)
        rom_32KB_block_erase(start_addr)

        pgm_data = string_to_table(value)
        if #pgm_data < 32 then
            pgm_data[#pgm_data+1] = 0
        end

        rom_program(start_addr, pgm_data)

        ret = rom_read_data(start_addr, 64)
        print(string.format("Read data at %02x ", start_addr):upper())
        printf.table(ret, true)

        start_addr = start_addr + block_32KB_len
    end
end

if false then
    local pgm_start_addr, pgm_raw_data
    local start_addr = 32*1024
    pgm_info = string_to_table("hw app2\nhell")

    pgm_start_addr, pgm_raw_data = iHEX.Read_File("hwatch_test_app2.hex")
    printf.table(pgm_raw_data, true)

    if #pgm_info < 32 then
        for i=#pgm_info+1, 32 do
            pgm_info[i] = 0
        end
    end
    tmp, pgm_info[34], pgm_info[33] = split_to_3byte(#pgm_raw_data)

    rom_32KB_block_erase(start_addr)

    rom_program(start_addr, pgm_raw_data)

    rom_program(start_addr + 32*1024 - 128, pgm_info)--write pgm info

    ret = rom_read_data(start_addr, #pgm_raw_data)
    printf.table(ret, true)

    ret = rom_read_data(start_addr + 32*1024 - 64, 64)
    printf.table(ret, true)

end

--Read all 1MB
if false then
    ret = rom_read_data(0, 1024*1024)
    if #ret > 0 then
        local file_w = io.open("W25Q80_All.bin", "wb")
        for i = 1, #ret do
            file_w:write(string.char(ret[i]))
        end
        file_w:close()
    else
        print("Read rom data fail")
    end
end
-- -----------------------------------------------
device_close()
